/* 
 * This file is part of Utilities for .NET (U4N)
 * Copyright (C) 2007 FutureFog (lammothmrc@yahoo.it)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Net;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WowAceUpdater.u4n
{
    /// <summary>
    /// Allows to sort a ListView according to the contents of its columns. 
    /// </summary>
    [
        ToolboxBitmap(typeof(ResourcesBase), "ListViewSortManager.bmp"),
        ProvideProperty("ColumnSortMode", typeof(ColumnHeader)),
        DefaultEvent("ItemCompare")
    ]
    public class ListViewSortManager : Component, IExtenderProvider
    {
        #region Class constructors
        private Dictionary<ColumnHeader, ColumnSortMode> _Dictionary;
        private ColumnHeader _SortColumn;
        private SortOrder _SortOrder;
        private ListView _ListView;
        private ImageList _ImageList;
        private SortArrowType _SortArrowType;
        private bool _UseWaitCursor;
        private bool _FullRowSorting;

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        public ListViewSortManager()
            : this(null)
        { }

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="listView">The ListView to be sorted.</param>
        public ListViewSortManager(ListView listView)
        {
            _Dictionary = new Dictionary<ColumnHeader, ColumnSortMode>();
            _ImageList = null;
            _SortColumn = null;
            _SortOrder = SortOrder.None;
            _UseWaitCursor = _FullRowSorting = true;

            SortArrowType = SortArrowType.Auto;
            ListView = listView;
            this.Disposed += new EventHandler(OnComponentDisposed);
        }

        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        ~ListViewSortManager()
        {
            OnComponentDisposed(this, null);
        }
        #endregion

        #region Exposed events
        /// <summary>
        /// Occurs when two ListViewItems are about to be compared in a column where ColumnSortModes is set to CustomWay.
        /// </summary>
        public event ItemCompareEventHandler ItemCompare = null;

        /// <summary>
        /// Occurs before starting a sorting operation on the ListView.
        /// </summary>
        public event SortingEventHandler BeforeSorting = null;

        /// <summary>
        /// Occurs when a sorting operation on the ListView has been completed.
        /// </summary>
        public event SortingEventHandler AfterSorting = null;
        #endregion

        #region Events handling
        /// <summary>
        /// Handles the ListView's ColumnClick event.
        /// </summary>
        /// <param name="sender">The ListView that has fired the event.</param>
        /// <param name="columnClickEventArgs">Event's arguments.</param>
        private void OnListViewColumnClick(object sender, ColumnClickEventArgs columnClickEventArgs)
        {
            Cursor cursor = null;
            if(_UseWaitCursor && (_ListView.FindForm() != null))
                cursor = _ListView.FindForm().Cursor;

            if(cursor != null)
                _ListView.FindForm().Cursor = Cursors.WaitCursor;

            Sort(columnClickEventArgs.Column);

            if(cursor != null)
                _ListView.FindForm().Cursor = cursor;
        }

        /// <summary>
        /// Event fired when this Component has been disposed.
        /// </summary>
        /// <param name="sender">This Component.</param>
        /// <param name="eventArgs">Event's arguments.</param>
        private void OnComponentDisposed(object sender, EventArgs eventArgs)
        {
            if(_ImageList != null)
                _ImageList.Dispose();
        }
        #endregion

        #region Public properties
        /// <summary>
        /// Gets the ColumnHeader of the column to sort.
        /// </summary>
        [Browsable(false)]
        public ColumnHeader SortColumn
        {
            get { return _SortColumn; }
        }

        /// <summary>
        /// Gets the index of the column to sort.
        /// </summary>
        [Browsable(false)]
        public int SortIndex
        {
            get { return CanExtend(_SortColumn) ? _SortColumn.Index : -1; }
        }

        /// <summary>
        /// Gets the sort order of the items in the ListView.
        /// </summary>
        [Browsable(false)]
        public SortOrder SortOrder
        {
            get { return _SortOrder; }
        }

        /// <summary>
        /// Gets or sets the ListView to be sorted.
        /// </summary>
        [
            Category("Sorting"),
            Description("Specifies the ListView to be sorted."),
            DefaultValue(null)
        ]
        public ListView ListView
        {
            get { return _ListView; }
            set
            {
                if(_ListView != null)
                {
                    _ListView.ColumnClick -= new ColumnClickEventHandler(OnListViewColumnClick);
                    DrawHeaderImages(SortArrowType.None, SortOrder.None);
                    SuspendSorting();
                }

                _ListView = value;

                if(_ListView == null)
                    _Dictionary.Clear();
                else
                {
                    _ListView.ColumnClick += new ColumnClickEventHandler(OnListViewColumnClick);
                    DrawHeaderImages(_SortArrowType, _SortOrder);

                    List<ColumnHeader> toRemove = new List<ColumnHeader>();
                    foreach(ColumnHeader column in _Dictionary.Keys)
                    {
                        if(!CanExtend(column))
                            toRemove.Add(column);
                    }
                    foreach(ColumnHeader column in toRemove)
                        _Dictionary.Remove(column);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating if to compare the subitems of the other columns, when two subitems are equal.
        /// </summary>
        [
            Category("Sorting"),
            Description("Specifies a value indicating if to compare the subitems of the other columns, when two subitems are equal."),
            DefaultValue(true)
        ]
        public bool FullRowSorting
        {
            get { return _FullRowSorting; }
            set { _FullRowSorting = value; }
        }

        /// <summary>
        /// Gets or sets the type of the sort arrow on the column header.
        /// </summary>
        [
            Category("Appearance"),
            Description("Specifies the type of the sort arrow on the column header."),
            DefaultValue(SortArrowType.Auto)
        ]
        public SortArrowType SortArrowType
        {
            get { return _SortArrowType; }
            set
            {
                _SortArrowType = value;

                if(_ImageList == null)
                {
                    _ImageList = new ImageList();
                    _ImageList.ImageSize = new Size(9, 8);
                    _ImageList.TransparentColor = Color.Magenta;
                    _ImageList.Images.Add(GetArrowBitmap(true));
                    _ImageList.Images.Add(GetArrowBitmap(false));
                }
                else
                {
                    _ImageList.Images[0] = GetArrowBitmap(true);
                    _ImageList.Images[1] = GetArrowBitmap(false);
                }

                if(_ListView != null)
                    DrawHeaderImages(_SortArrowType, _SortOrder);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating if to display a wait cursor during sorting.
        /// </summary>
        [
            Category("Appearance"),
            Description("Specifies a value indicating if to display a wait cursor during sorting."),
            DefaultValue(true)
        ]
        public bool UseWaitCursor
        {
            get { return _UseWaitCursor; }
            set { _UseWaitCursor = value; }
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Specifies the mode of sorting data in a column.
        /// </summary>
        /// <param name="columnHeader">The ColumnHeader of the associated ListView.</param>
        /// <param name="columnSortMode">The mode of sorting data.</param>
        [
            Category("Sorting"),
            Description("The mode of sorting data in the column."),
            DefaultValue(ColumnSortMode.CustomWay)
        ]
        public void SetColumnSortMode(ColumnHeader columnHeader, ColumnSortMode columnSortMode)
        {
            if(_Dictionary.ContainsKey(columnHeader))
                _Dictionary[columnHeader] = columnSortMode;
            else
                _Dictionary.Add(columnHeader, columnSortMode);
        }

        /// <summary>
        /// Returns the mode of sorting data in a column.
        /// </summary>
        /// <param name="columnHeader">The ColumnHeader of the associated ListView.</param>
        /// <returns>The mode of sorting data.</returns>
        [
            Category("Sorting"),
            Description("The mode of sorting data in the column."),
            DefaultValue(ColumnSortMode.CustomWay)
        ]
        public ColumnSortMode GetColumnSortMode(ColumnHeader columnHeader)
        {
            return _Dictionary.ContainsKey(columnHeader) ? _Dictionary[columnHeader] : ColumnSortMode.CustomWay;
        }

        /// <summary>
        /// Sorts the ListView according to a given column.
        /// </summary>
        /// <param name="sortIndex">The index of the column to sort.</param>
        public void Sort(int sortIndex)
        {
            if((_ListView != null) && (sortIndex >= 0) && (sortIndex < _ListView.Columns.Count))
                Sort(_ListView.Columns[sortIndex]);
        }

        /// <summary>
        /// Sorts the ListView according to a given column and sort order.
        /// </summary>
        /// <param name="sortIndex">The index of the column to sort.</param>
        /// <param name="sortOrder">The sort order of the items in the ListView.</param>
        public void Sort(int sortIndex, SortOrder sortOrder)
        {
            if((_ListView != null) && (sortIndex >= 0) && (sortIndex < _ListView.Columns.Count))
                Sort(_ListView.Columns[sortIndex], sortOrder);
        }

        /// <summary>
        /// Sorts the ListView according to a given column.
        /// </summary>
        /// <param name="sortColumn">The ColumnHeader of the column to sort.</param>
        public void Sort(ColumnHeader sortColumn)
        {
            SortOrder sortOrder = SortOrder.Ascending;

            if(sortColumn == _SortColumn)
                sortOrder = (_SortOrder == SortOrder.Ascending) ? SortOrder.Descending : SortOrder.Ascending;

            Sort(sortColumn, sortOrder);
        }

        /// <summary>
        /// Sorts the ListView according to a given column and sort order.
        /// </summary>
        /// <param name="sortColumn">The ColumnHeader of the column to sort.</param>
        /// <param name="sortOrder">The sort order of the items in the ListView.</param>
        public void Sort(ColumnHeader sortColumn, SortOrder sortOrder)
        {
            if(CanExtend(sortColumn))
            {
                if(this.BeforeSorting != null)
                    this.BeforeSorting(this, new SortingEventArgs(sortColumn, sortOrder));

                SortingEventArgs afterArgs = new SortingEventArgs(_SortColumn, _SortOrder);
                _SortColumn = sortColumn;
                _SortOrder = sortOrder;
                DrawHeaderImages(_SortArrowType, _SortOrder);

                if(!_Dictionary.ContainsKey(_SortColumn))
                    _Dictionary.Add(_SortColumn, ColumnSortMode.CustomWay);

                if(sortOrder == SortOrder.None)
                    _ListView.ListViewItemSorter = null;
                else
                {
                    _ListView.ListViewItemSorter = GetItemsComparer(_SortColumn);
                    if(this.AfterSorting != null)
                        this.AfterSorting(this, afterArgs);
                }
            }
        }

        /// <summary>
        /// Suspend the current ListView's sorting.
        /// </summary>
        public void SuspendSorting()
        {
            if(_ListView != null)
                _ListView.ListViewItemSorter = null;
        }

        /// <summary>
        /// Resumes the current ListView's sorting.
        /// </summary>
        public void ResumeSorting()
        {
            Sort(_SortColumn, _SortOrder);
        }

        /// <summary>
        /// Prevents the ListView from drawing until the EndUpdate method is called.
        /// </summary>
        public void BeginUpdate()
        {
            if(_ListView != null)
                _ListView.BeginUpdate();

            SuspendSorting();
        }

        /// <summary>
        /// Resumes drawing of the ListView after drawing is supended by the BeginUpdate method.
        /// </summary>
        public void EndUpdate()
        {
            ResumeSorting();

            if(_ListView != null)
                _ListView.EndUpdate();
        }

        /// <summary>
        /// Refresh images and text of the ColumnHeaders of the associated ListView.
        /// </summary>
        public void RefreshHeader()
        {
            SortArrowType = SortArrowType;
        }
        #endregion

        #region Windows API declares
        [DllImport("User32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

        [DllImport("User32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, ref HDITEM lParam);

        private const int HDI_FORMAT = 0x0004;
        private const int HDI_IMAGE = 0x0020;
        private const int HDF_LEFT = 0x0000;
        private const int HDF_RIGHT = 0x0001;
        private const int HDF_CENTER = 0x0002;
        private const int HDF_STRING = 0x4000;
        private const int HDF_BITMAP_ON_RIGHT = 0x1000;
        private const int HDF_IMAGE = 0x0800;
        private const int LVM_FIRST = 0x1000;
        private const int LVM_GETHEADER = LVM_FIRST + 31;
        private const int HDM_FIRST = 0x1200;
        private const int HDM_GETIMAGELIST = HDM_FIRST + 9;
        private const int HDM_SETIMAGELIST = HDM_FIRST + 8;
        private const int HDM_SETITEM = HDM_FIRST + 12;

        [StructLayout(LayoutKind.Sequential)]
        private struct HDITEM
        {
            public int mask;
            public int cxy;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string pszText;
            public IntPtr hbm;
            public int cchTextMax;
            public int fmt;
            public int lParam;
            public int iImage;
            public int iOrder;
        };
        #endregion

        #region Sort arrow drawing
        /// <summary>
        /// Returns an image representing the sort arrow.
        /// </summary>
        /// <param name="ascending">True or false for ascending or descending sorting.</param>
        /// <returns>The image representing the sort arrow.</returns>
        private Bitmap GetArrowBitmap(bool ascending)
        {
            Bitmap bmp = new Bitmap(9, 8);
            Graphics gfx = Graphics.FromImage(bmp);
            gfx.FillRectangle(Brushes.Magenta, 0, 0, 9, 8);

            SortArrowType sat;
            switch(_SortArrowType)
            {
                case SortArrowType.Standard:
                case SortArrowType.Flat:
                case SortArrowType.None:
                    sat = _SortArrowType;
                    break;

                default:
                    if(MiscTools.IsWindowsXpOrNewer)
                        sat = SortArrowType.Flat;
                    else
                        sat = SortArrowType.Standard;
                    break;
            }

            switch(sat)
            {
                case SortArrowType.Standard:
                    Pen lightPen = SystemPens.ControlLightLight;
                    Pen shadowPen = SystemPens.ControlDark;
                    if(ascending)
                    {
                        gfx.DrawLine(lightPen, 0, 7, 7, 7);
                        gfx.DrawLine(lightPen, 7, 7, 4, 0);
                        gfx.DrawLine(shadowPen, 3, 0, 0, 7);
                    }
                    else
                    {
                        gfx.DrawLine(lightPen, 4, 7, 7, 0);
                        gfx.DrawLine(shadowPen, 3, 7, 0, 0);
                        gfx.DrawLine(shadowPen, 0, 0, 7, 0);
                    }
                    break;

                case SortArrowType.Flat:
                    Pen flatPen = SystemPens.ControlDark;
                    if(ascending)
                    {
                        gfx.DrawLine(flatPen, 4, 1, 4, 2);
                        gfx.DrawLine(flatPen, 3, 2, 5, 2);
                        gfx.DrawLine(flatPen, 2, 3, 6, 3);
                        gfx.DrawLine(flatPen, 1, 4, 7, 4);
                        gfx.DrawLine(flatPen, 0, 5, 8, 5);
                    }
                    else
                    {
                        gfx.DrawLine(flatPen, 0, 1, 8, 1);
                        gfx.DrawLine(flatPen, 1, 2, 7, 2);
                        gfx.DrawLine(flatPen, 2, 3, 6, 3);
                        gfx.DrawLine(flatPen, 3, 4, 5, 4);
                        gfx.DrawLine(flatPen, 4, 5, 4, 4);
                    }
                    break;

                default:
                    break;
            }

            gfx.Dispose();
            return bmp;
        }

        /// <summary>
        /// Draws the sort arrow (or the original images) on the column headers.
        /// </summary>
        /// <param name="sortArrowType">The type of the sort arrow on the column header.</param>
        /// <param name="sortOrder">The sort order of the items in the ListView.</param>
        private void DrawHeaderImages(SortArrowType sortArrowType, SortOrder sortOrder)
        {
            IntPtr hListView = IntPtr.Zero;
            IntPtr hImageList = IntPtr.Zero;
            try
            {
                hListView = _ListView.Handle;
                hImageList = ((sortArrowType == SortArrowType.None) && (_ListView.SmallImageList != null)) ? _ListView.SmallImageList.Handle : _ImageList.Handle;
            }
            catch
            { }
            if((hListView == IntPtr.Zero) || (hImageList == IntPtr.Zero))
                return;

            IntPtr hHeader = SendMessage(hListView, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
            if(hHeader == IntPtr.Zero)
                return;

            if(hImageList != SendMessage(hHeader, HDM_GETIMAGELIST, IntPtr.Zero, IntPtr.Zero))
            {
                SendMessage(hHeader, HDM_SETIMAGELIST, IntPtr.Zero, hImageList);

                if(sortArrowType == SortArrowType.None)
                {
                    foreach(ColumnHeader column in _ListView.Columns)
                    {
                        string imageKey = column.ImageKey;
                        int imageIndex = column.ImageIndex;
                        column.ImageIndex = (column.ImageList == null) ? 0 : column.ImageList.Images.Count;
                        column.ImageIndex = imageIndex;
                        column.ImageKey = imageKey;
                    }
                }
            }

            if(sortArrowType != SortArrowType.None)
            {
                for(int i = 0; i < _ListView.Columns.Count; i++)
                {
                    HDITEM hd = new HDITEM();
                    hd.mask = HDI_IMAGE | HDI_FORMAT;
                    hd.fmt = HDF_STRING;
                    hd.iImage = -1;

                    switch(_ListView.Columns[i].TextAlign)
                    {
                        default:
                        case HorizontalAlignment.Left:
                            hd.fmt |= HDF_LEFT;
                            break;

                        case HorizontalAlignment.Center:
                            hd.fmt |= HDF_CENTER;
                            break;

                        case HorizontalAlignment.Right:
                            hd.fmt |= HDF_RIGHT;
                            break;
                    }

                    if((SortIndex == i) && (sortOrder != SortOrder.None))
                    {
                        hd.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
                        hd.iImage = (int)sortOrder - 1;
                    }

                    SendMessage(hHeader, HDM_SETITEM, new IntPtr(i), ref hd);
                }
            }
        }
        #endregion

        #region Private methods
        /// <summary>
        /// Returns the final result after a comparison.
        /// </summary>
        /// <param name="firstResult">The first obtained result.</param>
        /// <param name="sortColumn">The first column being sorted.</param>
        /// <param name="itemA">The first comparison ListViewItem.</param>
        /// <param name="itemB">The second comparison ListViewItem.</param>
        /// <returns>The final result after a comparison.</returns>
        private int GetFinalResult(int firstResult, ColumnHeader sortColumn, object itemA, object itemB)
        {
            if(_SortOrder == SortOrder.Descending)
                firstResult = -firstResult;

            if(_FullRowSorting && (firstResult == 0) && (sortColumn == _SortColumn))
            {
                for(int i = 0; (i < _ListView.Columns.Count) && (firstResult == 0); i++)
                {
                    if(i != sortColumn.Index)
                        firstResult = GetItemsComparer(_ListView.Columns[i]).Compare(itemA, itemB);
                }
            }

            return firstResult;
        }

        /// <summary>
        /// Returns an instance of the comparer used to sort a specific column.
        /// </summary>
        /// <param name="sortColumn">The column to sort.</param>
        /// <returns>The comparer used to sort the column.</returns>
        private IComparer GetItemsComparer(ColumnHeader sortColumn)
        {
            Type comparer;

            switch(GetColumnSortMode(sortColumn))
            {
                default:
                case ColumnSortMode.TextCaseSensitive:
                    comparer = typeof(ListViewSort_TextCaseSensitive);
                    break;

                case ColumnSortMode.TextCaseInsensitive:
                    comparer = typeof(ListViewSort_TextCaseInsensitive);
                    break;

                case ColumnSortMode.DateTime:
                    comparer = typeof(ListViewSort_DateTime);
                    break;

                case ColumnSortMode.Number:
                    comparer = typeof(ListViewSort_Number);
                    break;

                case ColumnSortMode.IPAddress:
                    comparer = typeof(ListViewSort_IPAddress);
                    break;

                case ColumnSortMode.IPv6Address:
                    comparer = typeof(ListViewSort_IPv6Address);
                    break;

                case ColumnSortMode.CustomWay:
                    comparer = typeof(ListViewSort_CustomWay);
                    break;
            }

            return (IComparer)Activator.CreateInstance(comparer, this, sortColumn);
        }
        #endregion

        #region Implementation of IExtenderProvider
        /// <summary>
        /// Returns a value indicating if an object is a ColumnHeader that can be extended in the designer.
        /// </summary>
        /// <param name="component">The object to check.</param>
        /// <returns>A value indicating if an object is a ColumnHeader that can be extended in the designer.</returns>
        public bool CanExtend(object component)
        {
            return (component is ColumnHeader) && (_ListView != null) && (_ListView.Equals((component as ColumnHeader).ListView));
        }
        #endregion

        #region Comparison types
        /// <summary>
        /// Allows to sort text in a case sensitive way.
        /// </summary>
        private class ListViewSort_TextCaseSensitive : IComparer
        {
            private ListViewSortManager _SortManager;
            private ColumnHeader _SortColumn;

            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="sortManager">The ListViewSortManager's instance.</param>
            /// <param name="sortColumn">The instance of the column being sorted.</param>
            public ListViewSort_TextCaseSensitive(ListViewSortManager sortManager, ColumnHeader sortColumn)
            {
                _SortManager = sortManager;
                _SortColumn = sortColumn;
            }

            /// <summary>
            /// Compares two object (ListViewItems).
            /// </summary>
            /// <param name="A">The first comparison ListViewItem.</param>
            /// <param name="B">The second comparison ListViewItem.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            public int Compare(object A, object B)
            {
                if((A == null) || (B == null))
                    return 0;

                try
                {
                    ListViewItem.ListViewSubItemCollection lvsA = (A as ListViewItem).SubItems;
                    ListViewItem.ListViewSubItemCollection lvsB = (B as ListViewItem).SubItems;

                    int index = _SortColumn.Index;
                    string sA = ((index >= 0) && (lvsA.Count > index)) ? lvsA[index].Text : string.Empty;
                    string sB = ((index >= 0) && (lvsB.Count > index)) ? lvsB[index].Text : string.Empty;

                    bool error = false;
                    int result = CompareStrings(sA, sB, ref error);

                    if(error)
                    {
                        if(_SortManager.ItemCompare != null)
                        {
                            ItemCompareEventArgs args = new ItemCompareEventArgs((ListViewItem)A, (ListViewItem)B, _SortColumn, ItemCompareCause.ComparingError);
                            _SortManager.ItemCompare(_SortManager, args);
                            result = args.ComparisonResult;
                        }
                    }

                    return _SortManager.GetFinalResult(result, _SortColumn, A, B);
                }
                catch
                {
                    return 0;
                }
            }

            /// <summary>
            /// Compares the text of two ListViewItems according to the contained data type. 
            /// </summary>
            /// <param name="A">The first comparison string.</param>
            /// <param name="B">The second comparison string.</param>
            /// <param name="error">A value indicating if an error has occurred.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            protected virtual int CompareStrings(string A, string B, ref bool error)
            {
                return string.Compare(A, B, false);
            }
        }

        /// <summary>
        /// Allows to sort text in a case insensitive way.
        /// </summary>
        private class ListViewSort_TextCaseInsensitive : ListViewSort_TextCaseSensitive
        {
            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="sortManager">The ListViewSortManager's instance.</param>
            /// <param name="sortColumn">The instance of the column being sorted.</param>
            public ListViewSort_TextCaseInsensitive(ListViewSortManager sortManager, ColumnHeader sortColumn)
                : base(sortManager, sortColumn)
            { }

            /// <summary>
            /// Compares the text of two ListViewItems according to the contained data type. 
            /// </summary>
            /// <param name="A">The first comparison string.</param>
            /// <param name="B">The second comparison string.</param>
            /// <param name="error">A value indicating if an error has occurred.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            protected override int CompareStrings(string A, string B, ref bool error)
            {
                return string.Compare(A, B, true);
            }
        }

        /// <summary>
        /// Allows to sort dates.
        /// </summary>
        private class ListViewSort_DateTime : ListViewSort_TextCaseSensitive
        {
            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="sortManager">The ListViewSortManager's instance.</param>
            /// <param name="sortColumn">The instance of the column being sorted.</param>
            public ListViewSort_DateTime(ListViewSortManager sortManager, ColumnHeader sortColumn)
                : base(sortManager, sortColumn)
            { }

            /// <summary>
            /// Compares the text of two ListViewItems according to the contained data type. 
            /// </summary>
            /// <param name="A">The first comparison string.</param>
            /// <param name="B">The second comparison string.</param>
            /// <param name="error">A value indicating if an error has occurred.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            protected override int CompareStrings(string A, string B, ref bool error)
            {
                try
                {
                    return DateTime.Parse(A).CompareTo(DateTime.Parse(B));
                }
                catch
                {
                    error = true;
                    return 0;
                }
            }
        }

        /// <summary>
        /// Allows to sort numbers.
        /// </summary>
        private class ListViewSort_Number : ListViewSort_TextCaseSensitive
        {
            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="sortManager">The ListViewSortManager's instance.</param>
            /// <param name="sortColumn">The instance of the column being sorted.</param>
            public ListViewSort_Number(ListViewSortManager sortManager, ColumnHeader sortColumn)
                : base(sortManager, sortColumn)
            { }

            /// <summary>
            /// Compares the text of two ListViewItems according to the contained data type. 
            /// </summary>
            /// <param name="A">The first comparison string.</param>
            /// <param name="B">The second comparison string.</param>
            /// <param name="error">A value indicating if an error has occurred.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            protected override int CompareStrings(string A, string B, ref bool error)
            {
                double result = 0;
                try
                {
                    result = double.Parse(A) - double.Parse(B);
                }
                catch
                {
                    error = true;
                }

                if(result > 0)
                {
                    return 1;
                }
                else if(result < 0)
                {
                    return -1;
                }
                else
                {
                    return 0;
                }
            }
        }

        /// <summary>
        /// Allows to sort IP addesses.
        /// </summary>
        private class ListViewSort_IPAddress : ListViewSort_TextCaseSensitive
        {
            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="sortManager">The ListViewSortManager's instance.</param>
            /// <param name="sortColumn">The instance of the column being sorted.</param>
            public ListViewSort_IPAddress(ListViewSortManager sortManager, ColumnHeader sortColumn)
                : base(sortManager, sortColumn)
            { }

            /// <summary>
            /// Compares the text of two ListViewItems according to the contained data type. 
            /// </summary>
            /// <param name="A">The first comparison string.</param>
            /// <param name="B">The second comparison string.</param>
            /// <param name="error">A value indicating if an error has occurred.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            protected override int CompareStrings(string A, string B, ref bool error)
            {
                try
                {
                    string[] splA = A.Split(new char[] { '.' });
                    string[] splB = B.Split(new char[] { '.' });

                    if((splA.Length != 4) || (splA.Length != 4))
                        throw new ArgumentException();

                    for(int i = 0; i <= 3; i++)
                    {
                        while(splA[i].Length < 3)
                            splA[i] = string.Concat("0", splA[i]);

                        while(splB[i].Length < 3)
                            splB[i] = string.Concat("0", splB[i]);
                    }

                    return string.Join(string.Empty, splA).CompareTo(string.Join(string.Empty, splB));
                }
                catch
                {
                    error = true;
                    return 0;
                }
            }
        }

        /// <summary>
        /// Allows to sort IPv6 addesses.
        /// </summary>
        private class ListViewSort_IPv6Address : ListViewSort_IPAddress
        {
            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="sortManager">The ListViewSortManager's instance.</param>
            /// <param name="sortColumn">The instance of the column being sorted.</param>
            public ListViewSort_IPv6Address(ListViewSortManager sortManager, ColumnHeader sortColumn)
                : base(sortManager, sortColumn)
            { }

            /// <summary>
            /// Compares the text of two ListViewItems according to the contained data type. 
            /// </summary>
            /// <param name="A">The first comparison string.</param>
            /// <param name="B">The second comparison string.</param>
            /// <param name="error">A value indicating if an error has occurred.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            protected override int CompareStrings(string A, string B, ref bool error)
            {
                try
                {
                    string[] parsed = { IPAddress.Parse(A).ToString(), IPAddress.Parse(B).ToString() };
                    int[] index = { parsed[0].LastIndexOf(':'), parsed[1].LastIndexOf(':') };
                    int result = string.Compare(parsed[0].Substring(0, index[0]), parsed[1].Substring(0, index[1]), true);

                    if(result == 0)
                        return base.CompareStrings(parsed[0].Substring(index[0] + 1), parsed[1].Substring(index[1] + 1), ref error);
                    else
                        return result;
                }
                catch
                {
                    error = true;
                    return 0;
                }
            }
        }

        /// <summary>
        /// Allows to sort text in a custom way. The ItemCompare event must be handled.
        /// </summary>
        private class ListViewSort_CustomWay : IComparer
        {
            private ListViewSortManager _SortManager;
            private ColumnHeader _SortColumn;

            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="sortManager">The ListViewSortManager's instance.</param>
            /// <param name="sortColumn">The instance of the column being sorted.</param>
            public ListViewSort_CustomWay(ListViewSortManager sortManager, ColumnHeader sortColumn)
            {
                _SortManager = sortManager;
                _SortColumn = sortColumn;
            }

            /// <summary>
            /// Performs a custom comparison over two object (ListViewItems).
            /// </summary>
            /// <param name="A">The first comparison ListViewItem.</param>
            /// <param name="B">The second comparison ListViewItem.</param>
            /// <returns>Less then zero if A is smaller then B, more than zero if A is greater then B, zero if they are equal.</returns>
            public int Compare(object A, object B)
            {
                if(_SortManager.ItemCompare == null)
                    return 0;

                try
                {
                    ItemCompareCause cause = (_SortColumn == _SortManager._SortColumn) ? ItemCompareCause.CustomWay : ItemCompareCause.FullRowSorting;
                    ItemCompareEventArgs args = new ItemCompareEventArgs((ListViewItem)A, (ListViewItem)B, _SortColumn, cause);
                    _SortManager.ItemCompare(_SortManager, args);
                    return _SortManager.GetFinalResult(args.ComparisonResult, _SortColumn, A, B);
                }
                catch
                {
                    return 0;
                }
            }
        }
        #endregion
    }

    /// <summary>
    /// Determines the mode of sorting data in the columns.
    /// </summary>
    public enum ColumnSortMode
    {
        /// <summary>
        /// Sorts text in a case insensitive way.
        /// </summary>
        TextCaseInsensitive,

        /// <summary>
        /// Sorts text in a case sensitive way.
        /// </summary>
        TextCaseSensitive,

        /// <summary>
        /// Sorts dates.
        /// </summary>
        DateTime,

        /// <summary>
        /// Sorts numbers.
        /// </summary>
        Number,

        /// <summary>
        /// Sorts IP addesses.
        /// </summary>
        IPAddress,

        /// <summary>
        /// Sorts IPv6 addesses.
        /// </summary>
        IPv6Address,

        /// <summary>
        /// Sorts items in a custom way. The ItemCompare event must be handled.
        /// </summary>
        CustomWay
    }

    /// <summary>
    /// Determines the display of the sort arrow on the column header.
    /// </summary>
    public enum SortArrowType
    {
        /// <summary>
        /// Dispalys a standard 3D arrow or a flat one according to the operating system.
        /// </summary>
        Auto,

        /// <summary>
        /// Displays a standard 3D arrow (Windows 2000 style).
        /// </summary>
        Standard,

        /// <summary>
        /// Displays a flat arrow (Windows XP style).
        /// </summary>
        Flat,

        /// <summary>
        /// Does not display any sort arrow.
        /// </summary>
        None
    }

    /// <summary>
    /// Identifies the cause due to which the event has been raised.
    /// </summary>
    public enum ItemCompareCause
    {
        /// <summary>
        /// The column has been marked as custom way sorted.
        /// </summary>
        CustomWay,

        /// <summary>
        /// The column must be sorted because the previous comparisons result equal.
        /// </summary>
        FullRowSorting,

        /// <summary>
        /// An error has occurred during items' comparison.
        /// </summary>
        ComparingError
    }

    /// <summary>
    /// Represents the method that will handle the ItemCompare event of a ListViewSortManager.
    /// </summary>
    /// <param name="sender">The ListViewSortManager that has raised the event.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void ItemCompareEventHandler(object sender, ItemCompareEventArgs e);

    /// <summary>
    /// Represents the method that will handle the BeforeSorting and AfterSorting events of a ListViewSortManager.
    /// </summary>
    /// <param name="sender">The ListViewSortManager that has raised the event.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void SortingEventHandler(object sender, SortingEventArgs e);

    /// <summary>
    /// Provides data for the ItemCompare event.
    /// </summary>
    public class ItemCompareEventArgs : EventArgs
    {
        private ListViewItem _ListViewItemA;
        private ListViewItem _ListViewItemB;
        private ColumnHeader _SortColumn;
        private ItemCompareCause _ItemCompareCause;
        private int _ComparisonResult;

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="listViewItemA">The first ListViewItem to compare.</param>
        /// <param name="listViewItemB">The second ListViewItem to compare.</param>
        /// <param name="sortColumn">The instance of the column being sorted.</param>
        /// <param name="itemCompareCause">The cause due to which the event has been raised.</param>
        public ItemCompareEventArgs(ListViewItem listViewItemA, ListViewItem listViewItemB, ColumnHeader sortColumn, ItemCompareCause itemCompareCause)
        {
            _ListViewItemA = listViewItemA;
            _ListViewItemB = listViewItemB;
            _SortColumn = sortColumn;
            _ItemCompareCause = itemCompareCause;
            _ComparisonResult = 0;
        }

        /// <summary>
        /// Gets the first ListViewItem to compare.
        /// </summary>
        public ListViewItem ListViewItemA
        {
            get { return _ListViewItemA; }
        }

        /// <summary>
        /// Gets the second ListViewItem to compare.
        /// </summary>
        public ListViewItem ListViewItemB
        {
            get { return _ListViewItemB; }
        }

        /// <summary>
        /// Gets the instance of the column being sorted.
        /// </summary>
        public ColumnHeader SortColumn
        {
            get { return _SortColumn; }
        }

        /// <summary>
        /// Gets the cause due to which the event has been raised.
        /// </summary>
        public ItemCompareCause ItemCompareCause
        {
            get { return _ItemCompareCause; }
        }

        /// <summary>
        /// Gets or sets a value: less then zero if item A is smaller then item B, more than zero if item A is greater then item B, zero if they are equal.
        /// </summary>
        public int ComparisonResult
        {
            get { return _ComparisonResult; }
            set { _ComparisonResult = value; }
        }
    }

    /// <summary>
    /// Provides data for the BeforeSorting and AfterSorting events.
    /// </summary>
    public class SortingEventArgs : EventArgs
    {
        private ColumnHeader _SortColumn;
        private SortOrder _SortOrder;

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="sortColumn">The ColumnHeader of the column to sort.</param>
        /// <param name="sortOrder">The sort order of the items in the ListView.</param>
        public SortingEventArgs(ColumnHeader sortColumn, SortOrder sortOrder)
        {
            _SortColumn = sortColumn;
            _SortOrder = sortOrder;
        }

        /// <summary>
        /// Gets the new SortColumn in the BeforeSorting event or the old one in the AfterSorting event.
        /// </summary>
        public ColumnHeader SortColumn
        {
            get { return _SortColumn; }
        }

        /// <summary>
        /// Gets the new SortIndex in the BeforeSorting event or the old one in the AfterSorting event.
        /// </summary>
        public int SortIndex
        {
            get { return (_SortColumn == null) ? -1 : _SortColumn.Index; }
        }

        /// <summary>
        /// Gets the new SortOrder in the BeforeSorting event or the old one in the AfterSorting event.
        /// </summary>
        public SortOrder SortOrder
        {
            get { return _SortOrder; }
        }
    }
}